/**
 * const vm = new Vue({
  el: '#app',
  data () {
    return {
      isShowImg1: false,
      isShowImg2: false,
      maxSize: 500,
      img1Url: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Flmg.jj20.com%2Fup%2Fallimg%2F1114%2F0512210S559%2F2105120S559-10-1200.jpg&refer=http%3A%2F%2Flmg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1672035348&t=c4d531ebe8bb3bbf5b8a4545e0f6c8ce', 
      img2Url: 'https://gimg2.baidu.com/image_search/src=http%3A%2F%2Flmg.jj20.com%2Fup%2Fallimg%2F1114%2F040221103339%2F210402103339-7-1200.jpg&refer=http%3A%2F%2Flmg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1672035348&t=75b1877d196f84edb4cb415275850ceb'
      // 需要执行 data() -> 挂载到 $data -> 挂在vm上
    }
  },
  template: `
    <div>
      <img v-if="isShowImg1" :src="img1Url" :width="maxSize"/>
      <img v-show="isShowImg2" :src="img2Url" :width="maxSize" />
    </div>
    <button @click="showImg1"> 显示图片1 </button>
    <button @click="showImg2"> 显示图片2 </button>
  `,
  methods: {
    showImg1() {
      this.isShowImg1 = !this.isShowImg1;
      // this 需要指向 vm 实例 -》不仅需要获取 methods 方法， 也需要获取 $data -> 能读取数据值 -> vm 容器
    },
    showImg2() {
      this.isShowImg2 = !this.isShowImg2;
    }
  }
})
 */

/**
 * 需要一个 showPool （详见：type.ts）
 * 
 * 还有 eventPool （详见：type.ts）
 * 
 *  其实可以使用 Map 数据类型 {dom: {}}
 */

var Vue = (function () {
  const Vue = function (options) {
    // 1. 对容器获取el
    this.$el = document.querySelector(options.el)
    this.$data = options.data()

    var recycles = {
      beforeCreate: options.beforeCreate.bind(this),
      create: options.create.bind(this),
      beforeMounted: options.beforeMounted.bind(this),
      mounted: options.mounted.bind(this),
    }

    recycles.beforeCreate()

    /** 为了实例中不暴露methods、template */
    this._init(this, options.template, options.methods, recycles)
  }

  Vue.prototype = {
    _init: function (vm, template, methods, recycles) {

      recycles.create()

      var container = document.createElement('div') // 删除template外层容器，采用自创建 (不能使用 文档碎片，由于需要使用 innerHtml 挂载子节点)
      container.innerHTML = template


      /** 为什么在此申明，不在initPool()中
       * 在其他函数中（initData 等）需要使用
       */
      var showPool = new Map()
      var eventPool = new Map()

      /** 首先处理数据劫持
       * showPool -》 update()中需要
       */
      initData(vm, showPool)
      // 初始化缓存
      initPool(container, methods, showPool, eventPool)
      // 绑定事件处理函数
      bindEvent(vm, eventPool)
      // 渲染
      render(vm, showPool, container, recycles)
    }
  }

  function initData(vm, showPool) {
    var _data = vm.$data

    for (var key in _data) {
      (function (key) {
        Object.defineProperty(vm, key, {
          get() {
            // 作了一层代理
            return _data[key]
          },
          set(newVal) {
            _data[key] = newVal

            update(vm, key, showPool)
          }
        })
      }(key))
    }
  }

  function initPool(container, methods, showPool, eventPool) {
    var _allNodes = container.getElementsByTagName('*')
    var dom = null

    for (var i = 0; i < _allNodes.length; i++) {
      dom = _allNodes[i];

      var vIfData = dom.getAttribute('v-if')
      var vShowData = dom.getAttribute('v-show')
      var vEventData = dom.getAttribute('@click')

      if (vIfData) {
        showPool.set(dom, { type: 'if', prop: vIfData })
        dom.removeAttribute('v-if')
      } else if (vShowData) {
        showPool.set(dom, { type: 'show', prop: vShowData })
        dom.removeAttribute('v-show')
      }

      if (vEventData) {
        eventPool.set(dom, methods[vEventData])
        dom.removeAttribute('@click')
      }
    }
  }

  function bindEvent(vm, eventPool) {
    for (var [dom, handler] of eventPool) {
      vm[handler.name] = handler;
      dom.addEventListener('click', vm[handler.name].bind(vm), false)
    }
  }

  function render(vm, showPool, container, recycles) {
    var _data = vm.$data
    var _el = vm.$el

    for (var [dom, info] of showPool) {
      switch (info.type) {
        case 'if':
          info.comment = document.createComment(['v-if'])
          !_data[info.prop] && (dom.parentNode.replaceChild(info.comment, dom))
          break;
        case 'show':
          !_data[info.prop] && (dom.style.display = 'none')
          break;
        default:
          break;
      }
    }

    recycles.beforeMounted()

    _el.appendChild(container)

    recycles.mounted()
  }

  // 还有个 update ，更新视图，即在initData 的 set 中处理
  function update(vm, key, showPool) {
    var _data = vm.$data;

    for (var [dom, info] of showPool) {
      if (info.prop === key) {
        switch (info.type) {
          case 'if':
            !_data[key]
              ? dom.parentNode.replaceChild(info.comment, dom)
              : info.comment.parentNode.replaceChild(dom, info.comment)
            break;
          case 'show':
            !_data[info.prop]
              ? (dom.style.display = 'none')
              : dom.removeAttribute('style')
            break;
          default:
            break;
        }
      }
    }
  }

  return Vue
}())

export default Vue